// 引入bpmn建模器
import BpmnModeler from 'bpmn-js/lib/Modeler';

// 引入属性解析文件和对应的解析器
import flowableDescriptor from '@/components/ProcessDesigner/bpmn/descriptor/flowable.json';
import { flowableExtension } from '@/components/ProcessDesigner/bpmn/moddle/flowable';

// 引入翻译模块
import translationsCN from '@/components/ProcessDesigner/bpmn/translate/zh';
import customTranslate from '@/components/ProcessDesigner/bpmn/translate/customTranslate';
import { FLOWABLE_PREFIX } from '../ProcessDesigner/bpmn/constant/constants';

// 引入流程图文件
import DefaultEmptyXML from '@/components/ProcessDesigner/bpmn/constant/emptyXml';
import BpmnPanelService from './components/Panel/service';
import { ref } from 'valtio';
import { message } from 'antd';
import type ElementRegistry from 'diagram-js/lib/core/ElementRegistry';
import type Modeling from "bpmn-js/lib/features/modeling/Modeling";
import { type Element } from 'bpmn-js/lib/model/Types';
import customControlsModule from './addition/customPanel';
import type BpmnFactory from 'bpmn-js/lib/features/modeling/BpmnFactory';
import { controls } from './controls';
import type Canvas from 'diagram-js/lib/core/Canvas';

const log = (...args: any) => {
  // console.log
};

/**
 * 添加解析器
 */
function getAdditionalModules() {
  const modules: any[] = [
    customControlsModule,
  ];

  // 支持flowable模式
  modules.push(flowableExtension);

  // 添加翻译模块
  const TranslateModule = {
    // translate: ["value", customTranslate(translations || translationsCN)] translations是自定义的翻译文件
    translate: ['value', customTranslate(translationsCN)],
  };
  modules.push(TranslateModule);

  // 添加模拟流转模块
  // modules.push(TokenSimulationModule);
  return modules;
}

/**
 * 添加解析文件
 */
function getModdleExtensions() {
  const extensions: any = {};
  extensions.flowable = flowableDescriptor;

  return extensions;
}

const bpmnPrefix = FLOWABLE_PREFIX;

export default class MiniBpmnService {
  // 一些数据放在这里，起一个容器的作用

  constructor() {
    log('MiniBpmnService constructor');
  }

  panel = new BpmnPanelService();

  modeler?: BpmnModeler;

  id: string = '';
  name: string = '';

  modelName = controls.model.name;

  getModeler() {
    if (!this.modeler) throw Error('modeler is not init');
    return this.modeler;
  }
  // window.bpmnInstance.bpmnFactory = modeler.get('bpmnFactory', true);
  // window.bpmnInstance.moddle = modeler.get('moddle', true);

  getElementRegistry() {
    return this.getModeler().get<ElementRegistry>('elementRegistry');
  }

  getModeling() {
    return this.getModeler().get<Modeling>('modeling', true);
  }

  getModdle() {
    return this.getModeler().get<BpmnFactory>('moddle', true);
  }

  /** 获取流程配置 */
  getProcessElement() {
    const newId = this.getModeler().getDefinitions()?.rootElements?.[0].id;
    if (!newId) return;
    // console.log('newId', newId);
    const processElement = this.getElementRegistry().get(newId) as Element | undefined;
    // console.log('processElement', processElement);
    return processElement;
  }

  /** 1.初始化bpmn容器 */
  mount(container: HTMLElement) {
    log('MiniBpmnService mount');

    this.modeler = ref(
      new BpmnModeler({
        container,
        additionalModules: getAdditionalModules(),
        moddleExtensions: getModdleExtensions(),
      })
    );

    this.panel.bindBpmnService(this);

  }

  initMetaData({ id, name }: { id: string, name: string }) {
    this.id = id;
    this.name = name;
  }

  async init(xml?: string) {
    if (!xml) {
      const newId = this.id || 'Process_' + new Date().getTime();
      const newName = this.name || '业务流程_' + new Date().getTime();
      // 必须初始化，否则无法创建节点
      await this.loadXml(DefaultEmptyXML(newId, newName, bpmnPrefix));
    } else {
      await this.loadXml(xml);
    }
  }

  /** 2.载入bpmn文件 */
  async loadXml(xml: string) {
    try {
      // this.modeler();
      await this.getModeler().importXML(xml);
    } catch (e) {
      message.error('流程图解析失败');
    }
  }

  async toXml() {
    const { error, xml } = await this.getModeler().saveXML({ format: true });
    if (error) {
      message.error('流程图导出失败');
    }

    return xml;
  }

  async saveXml() {
    const { error, xml } = await this.getModeler().saveXML({ format: true });
    // 读取异常时抛出异常
    if (error) {
      message.error('流程图导出失败');
    }
    const { href, filename } = setEncoded('BPMN', '流程建模', xml);
    downloadFunc(href, filename);

    /**
     * 根据所需类型进行转码并返回下载地址
     * @param type
     * @param filename
     * @param data
     */
    function setEncoded(type: string, filename = 'diagram', data: any) {
      const encodedData = encodeURIComponent(data);
      return {
        filename: `${filename}.${type}`,
        href: `data:application/${type === 'svg' ? 'text/xml' : 'bpmn20-xml'
          };charset=UTF-8,${encodedData}`,
        data,
      };
    }

    /**
     * 文件下载方法
     * @param href
     * @param filename
     */
    function downloadFunc(href: string, filename: string) {
      if (href && filename) {
        const a = document.createElement('a');
        a.download = filename; // 指定下载的文件名
        a.href = href; //  URL对象
        a.click(); // 模拟点击
        URL.revokeObjectURL(a.href); // 释放URL 对象
      }
    }
  }

  async clear() {
    this.getModeler().clear();

    await this.init();
    // @ts-expect-error
    this.getModeler().get<Canvas>("canvas").zoom(1, "auto");
  }

  unmount() {
    log('MiniBpmnService unmount');
    this.modeler?.destroy();
  }

  destroy() {
    log('MiniBpmnService destroy');
    this.panel.destroy();

    // 这个不再destroy中销毁，因为在unmount中已经销毁了，由视图层管理，遵循谁创建谁销毁原则
    // 但是destroy还有一个原则就是要清理所有的创建动作

    this.modeler?.destroy();
  }
}
